package main

import (
	"bytes"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strconv"

	"gitee.com/jason_elva8325/socket-quickstart/protocol"
)

const (
	DL_MSG_PRE = "DLMC00000000011.010"
)

var (
	listenHost  = flag.String("listen-host", "127.0.0.1", "TCP listen host")
	tcpPort     = flag.String("tcp-port", "3333", "TCP listen port")
	withSSL     = flag.Bool("with-ssl", false, "TCP with SSL")
	messageType = flag.String("message-type", "DL", "data message")
	pieceSize   = flag.Int("piece-size", 512, "piece per file byte size, must be between 256 and 8192") // 需要与服务器端一致
	outDir      = flag.String("out-dir", "", "file output dir")
	logger      *log.Logger
)

type bizResponse struct {
	PackageCount    int // 长度4
	PackageSequence int // 长度4
	DataPiece       []byte
}

type bizRequest struct {
	Command  string // 长度2
	DeviceID string // 长度12
	Version  string // 长度5
	FlowNum  int    // 长度4
}

func init() {
	// 全局日志对象定义
	logger = log.New(os.Stderr, "[file-client] ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
}

func main() {
	flag.Parse()

	var (
		conn net.Conn
		err  error
	)

	// 判断是否启用SSL
	if *withSSL {
		conn, err = tls.Dial("tcp", *listenHost+":"+*tcpPort, &tls.Config{
			InsecureSkipVerify: true,
		})
	} else {
		conn, err = net.Dial("tcp", *listenHost+":"+*tcpPort)
	}
	defer conn.Close()
	if err != nil {
		logger.Printf("Error connecting to %s.\n%s", conn.RemoteAddr().String(), err.Error())
	}
	logger.Printf("Connect to %s success.\n", conn.RemoteAddr().String())

	// 开始向服务器发送消息
	var message string
	switch *messageType {
	case "DL":
		message = DL_MSG_PRE
	default:
		logger.Println("invalid message type")
		os.Exit(1)
	}

	var totalPieces = 8192 // 可能的最大分片
	var currentPiece = 1   // 序列从1开始
	var outBuffer = new(bytes.Buffer)
	var bufferSize = *pieceSize + 4 + 4 + 4 + 4 + 2 + 2 // 服务器数据分片大小+总分片数量+当前分片序列+报文长度+校验位+报文头+报文尾

	for {
		if currentPiece <= totalPieces {
			// 将请求报文序列号拼接到预设请求报文结尾，并发送请求
			packR := protocol.Pack([]byte(message + fmt.Sprintf("%04d", currentPiece)))
			log.Printf("pack string is: %s", packR)
			_, err = conn.Write(packR)
			if err != nil {
				logger.Printf("Error to send message because of %s", err.Error())
			}

			// 开始接收来自服务器的响应
			buffer := make([]byte, bufferSize)
			_, err = conn.Read(buffer)
			if err == io.EOF {
				logger.Printf("%s connection closed.", conn.RemoteAddr().String())
				os.Exit(1)
			} else if err != nil {
				logger.Printf("%s connection err:\n%s\n", conn.RemoteAddr().String(), err.Error())
				os.Exit(1)
			}

			// 获取报文头中的数据部分长度，从而计算报文实际长度
			rptUseLength, err := strconv.Atoi(string(buffer[2:6]))
			if err != nil {
				logger.Printf("Report get use length fail: %v\n", err)
				os.Exit(1)
			}
			rptUseLength = rptUseLength + 2 + 4 + 4 + 2 // 数据部分长度+报文头+报文长度字段+校验位+报文尾

			// 报文解码
			rpt, err := protocol.Unpack(buffer[:rptUseLength])
			if err != nil {
				logger.Printf("Report unpark fail: %v\n", err)
			}
			logger.Printf("client receive report length is: %d", len(rpt))

			// 业务消息解码
			msg, err := unpack(rpt, logger)
			if err != nil {
				logger.Printf("Message unpark fail: %v\n", err)
				logger.Printf("Message is: %s", rpt)
				break
			}
			logger.Printf("\"full piece count\": %d， \"current piece sequence: %d\"", msg.PackageCount, msg.PackageSequence)
			totalPieces = msg.PackageCount
			currentPiece = msg.PackageSequence + 1
			outBuffer.Write(msg.DataPiece)
		} else {
			ioutil.WriteFile(*outDir+"out.txt", outBuffer.Bytes(), os.ModePerm)
			break
		}
	}
}

func unpack(messageBytes []byte, logger *log.Logger) (*bizResponse, error) {
	req := new(bizResponse)
	// 判断最小长度
	if len(messageBytes) < 8 {
		return nil, errors.New("invalid message format")
	}
	// 解析报文
	pCnt, _ := strconv.Atoi(string(messageBytes[:4]))
	req.PackageCount = pCnt
	pSeq, _ := strconv.Atoi(string(messageBytes[4:8]))
	req.PackageSequence = pSeq
	req.DataPiece = messageBytes[8:]
	return req, nil
}
